package extend.db;

import extend.db.base.Command;
import extend.db.base.ParsedSql;
import extend.db.util.DbUtil;
import java.lang.reflect.Constructor;
import java.sql.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 数据库连接基类
 *<B>说明：</B>
 *<pre>
 *  数据库连接基类，所有数据库连接都必须继承此类
 *</pre>
 *<B>示例：</B>
 *<pre>
 *  略
 *</pre>
 *<B>日志：</B>
 *<pre>
 *  略
 *</pre>
 *<B>注意事项：</B>
 *<pre>
 *  略
 *</pre>
 */
public class DbConnection
{
    /**
     * 数据库连接对象
     *<B>说明：</B>
     *<pre>
     *  略
     *</pre>
     */
    protected Connection conn;

    /**
     * 事务状态
     *<B>说明：</B>
     *<pre>
     *  略
     *</pre>
     */
    protected boolean transOn = false;


    /**
     * sql 处理命令
     *<B>说明：</B>
     *<pre>
     *  略
     *</pre>
     */
    private PreparedStatement stmt = null;

    /**
     * 数据库类型
     *<B>说明：</B>
     *<pre>
     *  略
     *</pre>
     */
    protected String dbType = "";

    /**
     * jdbc 数据库驱动类
     *<B>说明：</B>
     *<pre>
     *  例如:com.mysql.jdbc.Driver
     *</pre>
     */
    protected String driver = "";

    /**
     * jdbc 数据库连接
     *<B>说明：</B>
     *<pre>
     *  例如:jdbc:mysql://127.0.0.1:3306/hh0
     *</pre>
     */
    protected String dsn = "";

    /**
     * 数据库用户名
     *<B>说明：</B>
     *<pre>
     *  例如:admin
     *</pre>
     */
    protected String user = "";

    /**
     * 数据库用户密码
     *<B>说明：</B>
     *<pre>
     *  例如:admin
     *</pre>
     */
    protected String password = "";

    /**
     * 数据库表名前缀
     *<B>说明：</B>
     *<pre>
     *  例如:admin
     *</pre>
     */
    protected String prefix = "";


    /**
     * 构造方法
     *<B>说明：</B>
     *<pre>
     *  初始化数据库连接
     *</pre>
     * @param attrs 数据库连接配置
     */
    public DbConnection(Map<String,Object> attrs)
    {
        if (attrs != null) {
            DbUtil.attr(attrs,this);
        }
    }

    /**
     * 获取表名前缀
     *<B>说明：</B>
     *<pre>
     *  初始化数据库连接
     *</pre>
     * @return 表名前缀
     */
    public String getPrefix()
    {
        return this.prefix;
    }

    /**
     * 获取sql生成对象
     *<B>说明：</B>
     *<pre>
     *  略
     *</pre>
     * @return
     */
    public Builder getQueryBuilder()
    {
        return null;
    }

    /**
     * 初始化数据库连接
     *<B>说明：</B>
     *<pre>
     *  连接数据库
     *</pre>
     */
    private void initConnect() throws Exception
    {
        if (this.conn == null) {
            this.conn = this.connect();
        }
    }

    /**
     * 快速创建DbConnection对象
     *<B>说明：</B>
     *<pre>
     *  略
     *</pre>
     * @param options
     * @return 连接数据库对象
     */
    public static DbConnection make(Map<String,Object> options) throws Exception
    {

        // 获取当前类的包名（不包含自己）
        String packageName = new Object()    {
              public String getPackageName()
              {
                  Class onwClass = this.getClass();
                  Package dbPackage =  onwClass.getPackage();
                  return  dbPackage.getName();
              }
          }.getPackageName();

        StringBuilder dbClassName = new StringBuilder();
        String dbType = (String)options.get("dbType");
        // 判断dbType 是否命名空间,搜索"." 是否存在dbType
        if (dbType.indexOf(".") == -1) {
            dbClassName.append(packageName).append(".").append(dbType).append(".").append(DbUtil.upperCase(dbType)).append("Connection");
        } else {
            dbClassName.append(dbType);
        }

        try {
            // 实例化对象
            Class dbClass = Class.forName(dbClassName.toString());
            Class[] argtype = new Class[]{ Map.class };
            Object[] args = { options };//代表构造方法的参数值数组
            Constructor constructor = dbClass.getDeclaredConstructor(argtype); //获得构造方法，argtype是参数类型数组，我们这里代表的是参数只有一个String类型
            DbConnection dbConnection = (DbConnection)constructor.newInstance(args);

            return  dbConnection;
        } catch (Exception e) {
            throw new Exception("DbConnection make fail,class name:" + dbClassName);
        }
    }


    /**
     * 连接数据库
     *<B>说明：</B>
     *<pre>
     *  略
     *</pre>
     */
    public Connection connect() throws Exception
    {
        try {
            // 连续数据库
            Class.forName(this.driver);
            this.conn = DriverManager.getConnection(this.dsn, this.user, this.password);

            // 关闭自动提交事务
            return this.conn;
        } catch (Exception e) {
            e.printStackTrace();
            throw new Exception("连接数据库异常" + e.getStackTrace());
        }
    }

    /**
     * 关闭当前连接
     *<B>说明：</B>
     *<pre>
     *  略
     *</pre>
     */
    public boolean close()
    {
        try {
            if (this.conn != null) {
                this.conn.close();
            }

            return true;
        } catch (SQLException e) {
            // 记录sql 异常日志
            return false;
        }
    }

    /**
     * 执行更新操作
     *<B>说明：</B>
     *<pre>
     *  略
     *</pre>
     * @param command SQL命令对象
     */
    public int execute(Command command) throws Exception
    {
        this.initConnect();
        if (this.conn == null) {
            throw new SQLException("conn no");
        }

        if (this.stmt != null) {
            this.free();
        }

        try {
            ParsedSql parsedSql = new ParsedSql(command);
            this.stmt = this.conn.prepareStatement(parsedSql.getSql());
            parsedSql.parseSqlStatement(this.stmt);
            int rowCount = this.stmt.executeUpdate();

            return rowCount;
        } catch (Exception e) {
            // 记录日志2000011
            return 0;
        }


    }

    /**
     * 执行查询sql
     *<B>说明：</B>
     *<pre>
     *  略
     *</pre>
     * @param command SQL命令对象
     * @return 查询结果
     */
    public List<Map<String,Object>> query (Command command) throws Exception
    {

        this.initConnect();
        if (this.conn == null) {
            throw new SQLException("conn no");
        }

        if (this.stmt != null) {
            this.free();
        }

        try {
            ParsedSql parsedSql = new ParsedSql(command);
            this.stmt = this.conn.prepareStatement(parsedSql.getSql());
            parsedSql.parseSqlStatement(this.stmt);
            ResultSet rs  = this.stmt.executeQuery();

            return this.resultSetToList(rs);
        } catch (Exception e) {
            // 记录日志2000011
            e.printStackTrace();
        } finally {
            this.free();
        }

        return null;
    }

    /**
     * 开始事务
     *<B>说明：</B>
     *<pre>
     *  略
     *</pre>
     * @return boolean
     */
    public boolean beginTransaction()
    {
        try {
            this.initConnect();
            if (this.conn == null) {
                return false;
            }

            if (this.transOn == true) {
                return true;
            }

            // 关闭自动事务提交,相当于开启事务控制
            this.conn.setAutoCommit(false);
            this.transOn = true;

            return true;
        } catch (Exception e) {
            return false;
        }
    }

    /**
     * 提交事务
     *<B>说明：</B>
     *<pre>
     *  略
     *</pre>
     * @return boolean
     */
    public boolean commit()
    {
        try {
            if (this.conn == null || this.transOn == false) {
                return false;
            }

            // 关闭自动事务提交,相当于开启事务控制
            this.conn.commit();
            this.clearTrans();

            return true;
        } catch (Exception e) {
            return false;
        }
    }

    /**
     * 回滚事务
     *<B>说明：</B>
     *<pre>
     *  略
     *</pre>
     * @return boolean
     */
    public boolean rollback()
    {
        try {
            if (this.conn == null || this.transOn == false) {
                return false;
            }

            // 关闭自动事务提交,相当于开启事务控制
            this.conn.rollback();
            this.clearTrans();

            return true;
        } catch (Exception e) {
            return false;
        }
    }

    /**
     * 清空事务参数
     *<B>说明：</B>
     *<pre>
     *  略
     *</pre>
     */
    private void clearTrans()
    {
        this.transOn = false;
    }

    /**
     * 释放当前sql操作资源
     *<B>说明：</B>
     *<pre>
     *  略
     *</pre>
     */
    private boolean free()
    {
        try {
            if (this.stmt != null) {
                this.stmt.close();
            }

            return true;
        } catch (SQLException e) {
            // 记录sql 异常日志
            return false;
        }
    }

    /**
     * 结果集转成list
     *<B>说明：</B>
     *<pre>
     *  略
     *</pre>
     */
    public static List<Map<String,Object>> resultSetToList(ResultSet rs) throws SQLException
    {
        List<Map<String,Object>> results = new ArrayList<Map<String,Object>>();
        ResultSetMetaData rsmd = rs.getMetaData();
        int colCount = rsmd.getColumnCount();
        List<String> colNameList = new ArrayList<String>();

        for (int i=0;i<colCount;i++) {
            colNameList.add(rsmd.getColumnName(i+1));
        }

        while (rs.next()) {
            Map map=new HashMap<String, Object>();
            for (int i=0;i<colCount;i++) {
                String key = colNameList.get(i);
                Object value=rs.getString(colNameList.get(i));
                map.put(key, value);
            }

            results.add(map);
        }

        return results;
    }
}
